// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;

namespace dnSpy.Roslyn.Internal.SmartIndent {
	interface IIndentationService : ILanguageService {
		/// <summary>
		/// Determines the desired indentation of a given line.
		/// </summary>
		IndentationResult GetIndentation(ParsedDocument document, int lineNumber, IndentationOptions options,
			CancellationToken cancellationToken);
	}

	static class IIndentationServiceExtensions {
		/// <summary>
		/// Get's the preferred indentation for <paramref name="token"/> if that token were on its own line.  This
		/// effectively simulates where the token would be if the user hit enter at the start of the token.
		/// </summary>
		public static string GetPreferredIndentation(this SyntaxToken token, ParsedDocument document, IndentationOptions options, CancellationToken cancellationToken) {
			var tokenLine = document.Text.Lines.GetLineFromPosition(token.SpanStart);
			var firstNonWhitespacePos = tokenLine.GetFirstNonWhitespacePosition();
			Contract.ThrowIfNull(firstNonWhitespacePos);
			if (firstNonWhitespacePos.Value == token.SpanStart) {
				// token was on it's own line.  Start the end delimiter at the same location as it.
				return tokenLine.Text!.ToString(TextSpan.FromBounds(tokenLine.Start, token.SpanStart));
			}

			// Token was on a line with something else.  Determine where we would indent the token if it was on the next
			// line and use that to determine the indentation of the final line.

			var annotation = new SyntaxAnnotation();
			var newToken = token.WithAdditionalAnnotations(annotation);

			var syntaxGenerator = document.LanguageServices.GetRequiredService<SyntaxGeneratorInternal>();
			newToken = newToken.WithLeadingTrivia(
				newToken.LeadingTrivia.Add(syntaxGenerator.EndOfLine(options.FormattingOptions.NewLine)));

			var newRoot = document.Root.ReplaceToken(token, newToken);
			var newDocument = document.WithChangedRoot(newRoot, cancellationToken);

			var newTokenLine =
				newDocument.Text.Lines.GetLineFromPosition(newRoot.GetAnnotatedTokens(annotation).Single().SpanStart);

			var indenter = document.LanguageServices.GetRequiredService<IIndentationService>();
			var indentation = indenter.GetIndentation(newDocument, newTokenLine.LineNumber, options, cancellationToken);

			return indentation.GetIndentationString(
				newDocument.Text,
				options.FormattingOptions.UseTabs,
				options.FormattingOptions.TabSize);
		}
	}
}
